#include "soc_adc.h"
#include "main.h"
#include "log/logger.h"

#ifdef HAL_ADC_MODULE_ENABLED

#define SOC_ADC_WAIT_TIMEOUT 0xffffff

static inline uint32_t __GetChannelSingleDiff(ADC_TypeDef *ADCx, uint32_t Channel)
{
    return (uint32_t)(READ_BIT(ADCx->DIFSEL, (Channel & ADC_SINGLEDIFF_CHANNEL_MASK)));
}

SOC_Adc::SOC_Adc(AdcDevId id, void *handle)
{
    InitPeripheral(id, this, handle);
}

SOC_Adc::~SOC_Adc()
{
}

void SOC_Adc::Calib()
{
    CalibAdcImpl();
}

void SOC_Adc::StartAdcAsync()
{
    auto h = GetHandleAs<ADC_HandleTypeDef>(m_devId);
    HAL_ADC_Start(h);
}

int SOC_Adc::GetValueAsync()
{
    auto h = GetHandleAs<ADC_HandleTypeDef>(m_devId);
    return HAL_ADC_GetValue(h);
}

int SOC_Adc::GetValue(int ch)
{
    auto h = GetHandleAs<ADC_HandleTypeDef>(m_devId);
    int val = 0;

    ConfigAdcCh(h, ch);

    HAL_ADC_Start(h);
    HAL_ADC_PollForConversion(h, SOC_ADC_WAIT_TIMEOUT);
    val = HAL_ADC_GetValue(h);
    
    HAL_ADC_Stop(h);

    return val;
}

void SOC_Adc::StartAllAdcPoll(uint16_t *buffer, int size)
{
    if(buffer == nullptr || size == 0)
        return;
    
    auto h = GetHandleAs<ADC_HandleTypeDef>(m_devId);
    for(int i = 0; i < size; i++)
    {
        HAL_ADC_Start(h);
        HAL_ADC_PollForConversion(h, SOC_ADC_WAIT_TIMEOUT);
        int v = HAL_ADC_GetValue(h);
        buffer[i] = v;
    }
    HAL_ADC_Stop(h);
}

void SOC_Adc::StopAdc()
{
    auto h = GetHandleAs<ADC_HandleTypeDef>(m_devId);
    HAL_ADC_Stop(h);
}

void SOC_Adc::StartAdc_IT(int ch)
{
    auto h = GetHandleAs<ADC_HandleTypeDef>(m_devId);
    
    ConfigAdcCh(h, ch);
    HAL_ADC_Start_IT(h);
}

void SOC_Adc::StopAdc_IT()
{
    auto h = GetHandleAs<ADC_HandleTypeDef>(m_devId);
    
    HAL_ADC_Stop_IT(h);
}

void SOC_Adc::StartAdc_DMA(int chCount)
{
    StartAdc_DMA_NS(chCount, 1);
}

void SOC_Adc::StartAdc_DMA_NS(int chCount, int sampleCount)
{
    if(chCount <= 0)
        return;
    if(sampleCount > SOC_ADC_SAMPLE_COUNT_MAX)
        return;

    auto h = GetHandleAs<ADC_HandleTypeDef>(m_devId);
    if(m_buffer_DMA == nullptr)
    {
        int total = chCount * 2;
        total *= sampleCount;

        m_buffer_DMA = new uint16_t[total];
        memset((void*)m_buffer_DMA, 0, total);
    }

    m_chCount = chCount;
    m_sampleCount = sampleCount;
    HAL_ADC_Start_DMA(h, (uint32_t*)m_buffer_DMA, chCount * sampleCount);
}

void SOC_Adc::StopAdc_DMA()
{
    auto h = GetHandleAs<ADC_HandleTypeDef>(m_devId);

    HAL_ADC_Stop_DMA(h);
}

int SOC_Adc::GetDMAValue(int index)
{
    if(m_buffer_DMA == nullptr)
        return -1;
    if(m_chCount == 0)
        return -1;
    if(m_sampleCount <= 0)
        return -1;
    if(!(index >= 0 && index < m_chCount))
        return -1;
    
    if(m_sampleCount == 1)
    {
        return m_buffer_DMA[index];
    }
    else
    {
        int sum = 0;

        for(int i = 0; i < m_sampleCount; i++)
        {
            sum += m_buffer_DMA[i*m_chCount + index];
        }

        return sum / m_sampleCount;
    }
}

uint16_t *SOC_Adc::GetDMABuffer()
{
    return m_buffer_DMA;
}

void SOC_Adc::SetAdcConvertOverCallBack(const IF_SOC_Adc::ADC_CONVERT_OVER_IRQ_FUNC &&func)
{
    m_convertOverIrqFunc = func;
}

void SOC_Adc::Raise_ConvertOver_IRQ()
{
    if(m_convertOverIrqFunc == nullptr)
        return;

    auto h = GetHandleAs<ADC_HandleTypeDef>(m_devId);        
    m_convertOverIrqFunc(*this, m_lastConfigCh, HAL_ADC_GetValue(h));
}

/// @brief 通道轮询模式下使用
/// @param handle 
/// @param ch 
void SOC_Adc::ConfigAdcCh(void *handle, int ch)
{
    auto h = (ADC_HandleTypeDef*)handle;
    ADC_ChannelConfTypeDef sConfig = {0};
    
    m_lastConfigCh = ch;
    HAL_ADC_Stop(h);

#if defined STM32F0
    sConfig.Channel = ch;            
    h->Instance->CHSELR = 0;                        
    sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;//ADC_RANK_NONE;  
    sConfig.SamplingTime = ADC_GET_SAMPLINGTIME(h);//ADC_SAMPLETIME_1CYCLE_5;      

    if (HAL_ADC_ConfigChannel(h, &sConfig) != HAL_OK)             
    {
        //Error_Handler();
    }   
#elif defined STM32F1
    sConfig.Channel = ch;    
    sConfig.Rank = ADC_REGULAR_RANK_1;  
    sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5; //怎么获取SamplingTime??? 
                   
    if (HAL_ADC_ConfigChannel(h, &sConfig) != HAL_OK)             
    {
        //Error_Handler();
    }
#elif defined STM32G4 || defined STM32F4
    sConfig.Channel = ch;   
    sConfig.Rank = ADC_REGULAR_RANK_1;  
    sConfig.SamplingTime = LL_ADC_GetChannelSamplingTime(h->Instance, ch); 
    sConfig.SingleDiff = __GetChannelSingleDiff(h->Instance, ch);
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
                   
    if (HAL_ADC_ConfigChannel(h, &sConfig) != HAL_OK)             
    {
        //Error_Handler();
    } 
#endif   
}

void SOC_Adc::CalibAdcImpl()
{    
    auto h = GetHandleAs<ADC_HandleTypeDef>(m_devId);
    // auto adc = __GetChannelSingleDiff(h->Instance, );
    if (HAL_ADCEx_Calibration_Start(h, ADC_SINGLE_ENDED) != HAL_OK)
    {
        Error_Handler();
    }
}

#endif 